package com.lemon.exam.service.impl;

import com.lemon.exam.common.crud.BaseServiceImpl;
import com.lemon.exam.common.entity.view.ViewClassGradeUserJob;
import com.lemon.exam.common.entity.vo.SelectVO;
import com.lemon.exam.common.util.SecurityUtil;
import com.lemon.exam.common.web.Param;
import com.lemon.exam.entity.po.ClassPO;
import com.lemon.exam.repository.ClassRepository;
import com.lemon.exam.service.IClassService;
import com.lemon.exam.service.IGradeService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.relational.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 班级表 服务实现类
 *
 * @author Lemon
 * @since 2025/04/20 22:06:24
 */
@Slf4j
@Service
public class ClassServiceImpl extends BaseServiceImpl<ClassPO, Long, ClassRepository> implements IClassService {
    private static final String GRADE_CLASS_NAME = "%s [%s]";

    @Resource
    private IGradeService gradeService;

    @Override
    public Flux<String> findClassNameByUserId(Long userId) {
        return repository.findClassByUserId(userId).collectList().flatMapMany(BUILD_CLASS_NAME_GRADE_NAME);
    }

    /**
     * 构建班级名称
     */
    private static final Function<List<ViewClassGradeUserJob>, Publisher<? extends String>> BUILD_CLASS_NAME_GRADE_NAME = list -> Flux.fromIterable(groupByGradeId(list).entrySet()).map(entry -> GRADE_CLASS_NAME.formatted(
            entry.getValue().get(0).getGradeName(),
            entry.getValue().stream()
                    .map(ViewClassGradeUserJob::getClassName)
                    .sorted() // 班级名称排序
                    .collect(Collectors.joining(", "))));

    /**
     * 按年级分组
     */
    private static Map<Long, List<ViewClassGradeUserJob>> groupByGradeId(List<ViewClassGradeUserJob> list) {
        return list.stream()
                .sorted(Comparator.comparing(ViewClassGradeUserJob::getGradeId))
                .collect(Collectors.groupingBy(
                        ViewClassGradeUserJob::getGradeId,
                        LinkedHashMap::new, // 保持插入顺序
                        Collectors.toList()
                ));
    }

    @Override
    public Mono<Page<?>> page(Param param) {
        return Mono.zip(param.getCriteria(), super.logicDeleteCriteria())
                .map(tuple -> tuple.getT1().and(tuple.getT2()))
                .zipWith(param.getPageRequest())
                .flatMap(tuple -> Mono.zip(
                                template.select(Query.query(tuple.getT1()).with(tuple.getT2()), this.entityClass).collectList().subscribeOn(Schedulers.parallel()),
                                template.count(Query.query(tuple.getT1()), this.entityClass).subscribeOn(Schedulers.parallel())
                        )
                        .flatMap(buildPage(tuple.getT2())));
    }

    /**
     * 构建分页结果
     *
     * @param pageRequest
     * @return
     */
    private Function<Tuple2<List<ClassPO>, Long>, Mono<? extends PageImpl<?>>> buildPage(PageRequest pageRequest) {
        return tuple -> {
            List<ClassPO> list = tuple.getT1();
            if (CollectionUtils.isEmpty(list)) {
                return Mono.just(new PageImpl<>(new ArrayList<>(0), pageRequest, tuple.getT2()));
            }
            List<Long> gradeIdList = list.parallelStream().map(ClassPO::getGradeId).toList();
            return gradeService.findGradeIdGradeNameByGradeId(gradeIdList).map(grade -> {
                list.forEach(item -> item.setGradeName(grade.get(item.getGradeId())));
                return new PageImpl<>(new ArrayList<>(list), pageRequest, tuple.getT2());
            });
        };
    }

    @Override
    public Flux<SelectVO> findGradeSelect() {
        return gradeService.findGradeIdGradeName().map(gradePO -> new SelectVO(gradePO.getId().toString(), gradePO.getName()));
    }

    @Override
    public Mono<String> insert(ClassPO param) {
        return SecurityUtil.getUsername().flatMap(username -> {
            param.setCreateBy(username);
            return super.insert(param);
        });
    }

    @Override
    public Mono<String> update(ClassPO param) {
        return SecurityUtil.getUsername().flatMap(username -> {
            param.setUpdateBy(username);
            return super.update(param);
        });
    }

    @Override
    public Flux<ViewClassGradeUserJob> findAllClassGrade() {
        return repository.findAllClassGrade();
    }

    @Override
    public Mono<Map<Long, String>> findClassNameGradeNameById(List<Long> idList) {
        return Mono.defer(() -> {
            if (CollectionUtils.isEmpty(idList)) {
                return Mono.just(Collections.emptyMap());
            }
            return findClassNameGradeNameByIdList(idList);
        });
    }

    /**
     * 根据ID列表查询班级名称
     *
     * @param idList
     * @return
     */
    private Mono<Map<Long, String>> findClassNameGradeNameByIdList(List<Long> idList) {
        return repository.findClassNameGradeNameById(idList)
                .collect(Collectors.toMap(
                        ViewClassGradeUserJob::getClassId,
                        item -> GRADE_CLASS_NAME.formatted(item.getClassName(), item.getGradeName()),
                        (existing, replacement) -> existing  // 保留已有值
                ))
                .defaultIfEmpty(Collections.emptyMap());
    }
}
